/*
 *
 * Copyright(c) 2018-2019 chj <1019487871@qq.com>
 * QQ: 1019487871
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include "los.h"
 
struct los_sem_lcb *
los_sem_create(struct los_sem_lcb *sem,int lamps_limit,int init_lamp)
{
    if(sem == NULL)
        return NULL;
    sem->lamps_limit = lamps_limit;
    sem->lamp_counter = init_lamp;
    sem->sem_sync = NULL;
    return sem;
}
 
void
los_sem_post(struct los_sem_lcb *sem)
{
    struct los_thread_t *thread = NULL;

    if(sem == NULL)
        return;
    los_int_save_asyn();
    if(sem->sem_sync == NULL)     
    {
        if(sem->lamps_limit == -1)       
        {
            if(sem->lamp_counter != CN_LIMIT_SINT32)   
                sem->lamp_counter ++;                  
        }
        else if(sem->lamp_counter < sem->lamps_limit )
            sem->lamp_counter++;                      
    } 
    else      
    {
        thread = sem->sem_sync;
        sem->sem_sync = thread->sync_next;
        if(thread->thread_status & CN_STS_SYNC_TIMEOUT)
            los_resume_delay(thread);   
        thread->thread_status = CN_STS_THREAD_READY;
        los_thread_ready(thread->prio);
    }
    los_int_restore_asyn();
} 

bool_t
los_sem_pend(struct los_sem_lcb *sem,uint32_t timeout)
{
    bool_t lamp = false;

    if(sem == NULL)
    {
        return false;
    }
    los_int_save_asyn();
    if(sem->lamps_limit == -1)   
    {
        if(sem->lamp_counter != 0) 
            sem->lamp_counter--;   
        lamp = true;
    }
    else                           
    {
        if(sem->lamp_counter > 0)  
        {
            lamp = true;            
            sem->lamp_counter--;   
        }
        else                       
            lamp = false;           
    }

    if(lamp == true)
    {
        los_int_restore_asyn();  
        return true;    
    }
    else if(timeout == 0)
    {
        los_int_restore_asyn();  
        return false;   
    }
    los_add_running_to_block(&(sem->sem_sync),CN_BLOCK_FIFO,
                                            timeout,CN_STS_WAIT_SEM);
    los_int_restore_asyn();  
    if(los_get_wakeup_from()& CN_STS_SYNC_TIMEOUT)
    {
        return false;
    }
    else
    {
        return true;
    }
}
 
struct los_mutex_lcb *
los_mutex_create(struct los_mutex_lcb *mutex,uint16_t prio_ceiling)
{
    if(mutex == NULL ||
            prio_ceiling >= (LOS_THREAD_X_BIT*LOS_THREAD_Y_BIT))
        return NULL;
    if(!los_thread_is_null(prio_ceiling))
        return NULL;
    mutex->enable = 0;   
    mutex->mutex_sync = NULL;
    mutex->prio_bak = 0xffff;
    mutex->prio_ceiling = prio_ceiling;
    los_set_thread_placeholder(prio_ceiling);
    return mutex;
}
 
void 
los_mutex_post(struct los_mutex_lcb *mutex)
{
    struct los_thread_t *thread = NULL;

    if(mutex == NULL)
        return;
    if(mutex->owner != los_get_running_thread())
        return;
    los_int_save_asyn();
    if(mutex->enable > 0)
        mutex->enable--;
    if(mutex->enable == 0)
    {
        if(mutex->mutex_sync == NULL)     
        {
            mutex->owner = NULL;
        } 
        else      
        {
            thread = mutex->mutex_sync;
            thread->sync_head = NULL;
            mutex->owner = thread;
            mutex->enable = 1;
            if(thread->sync_next == thread)
            {
                mutex->mutex_sync = NULL;
            }
            else
            {
                mutex->mutex_sync = thread->sync_next;
                thread->sync_next->sync_prev = thread->sync_prev;
                thread->sync_prev->sync_next = thread->sync_next;
                thread->sync_next = NULL;
                thread->sync_prev = NULL;
            }
            if(thread->thread_status & CN_STS_SYNC_TIMEOUT)
                los_resume_delay(thread);    
            thread->thread_status = CN_STS_THREAD_READY;
            thread->wakeup_from = CN_STS_WAIT_MUTEX;
            los_thread_ready(thread->prio);
        }
        if(mutex->prio_bak == 0xffff)
            return;/*if reach here,that is a bug*/
        los_change_prio_for_ipc(mutex->prio_bak);
    }
    los_int_restore_asyn();
}
 
bool_t
los_mutex_pend(struct los_mutex_lcb *mutex,uint32_t timeout)
{
    bool_t lamp = false;
    atom_low_t low = 0;
    if(mutex == NULL)
        return false;
    if(los_get_running_thread() == mutex->owner)
    {
        mutex->enable++;
        return true;
    }
    los_int_save_asyn();
    if(mutex->enable == 0)  
    {
        lamp = true;
        mutex->enable = 1;
        mutex->owner = los_get_running_thread();
    }
    else                      
    {
        lamp = false;
    }

    if(lamp == true)
    {
        mutex->prio_bak = mutex->owner->prio;
        los_change_prio_for_ipc(mutex->prio_ceiling);
        los_int_restore_asyn();  
        return true;   
    }
    else if(timeout == 0)
    {
        los_int_restore_asyn();  
        return false;   
    }
    los_add_running_to_block(&(mutex->mutex_sync),CN_BLOCK_PRIO,
                                            timeout,CN_STS_WAIT_MUTEX);
    los_int_restore_asyn();
    if(los_get_wakeup_from() & CN_STS_SYNC_TIMEOUT)
    {
        return false;
    }
    else
    {
        low = losbsp_low_atom_start();
        mutex->owner = los_get_running_thread();
        mutex->prio_bak = mutex->owner->prio;
        los_change_prio_for_ipc(mutex->prio_ceiling);
        losbsp_low_atom_end(low);
        return true;
    }
}
 
int
los_msg_create(struct los_msg_lcb *msg)
{
    if(msg==NULL)
        return -1;
    if(msg->fifo.buffer.buffer==NULL || msg->fifo.buffer.size==0)
        return -1;
    los_sem_create(&msg->sem,0x7fffffff,0);
    los_fifo_init(&msg->fifo);
    return 0;
}

uint32_t
los_msg_push(struct los_msg_lcb *msg,void *data,uint32_t size)
{
    uint32_t len = 0;
    atom_low_t low = 0;
    if(msg==NULL)
        return 0;
    if(msg->fifo.buffer.buffer==NULL || msg->fifo.buffer.size==0)
        return 0;
    low = losbsp_low_atom_start();
    len = los_fifo_push(&msg->fifo, size, (uint8_t*)data);
    losbsp_low_atom_end(low);
    los_sem_post(&msg->sem);
    return len;
}

uint32_t
los_msg_pop(struct los_msg_lcb *msg,void *data,uint32_t size,uint32_t timeout)
{
    atom_low_t low = 0;
    uint32_t len = 0;
    uint32_t data_len = 0;
    if(msg==NULL)
        return 0;
    if(msg->fifo.buffer.buffer==NULL || msg->fifo.buffer.size==0)
        return 0;
    los_int_save_asyn();
    data_len = los_fifo_get_data_length(&msg->fifo);
    if(data_len==0)
    {
        msg->sem.lamp_counter = 0;
        los_sem_pend(&msg->sem,timeout);
        los_int_restore_asyn();
        data_len = los_fifo_get_data_length(&msg->fifo);
    }
    if(size>data_len)
        size = data_len;
    low = losbsp_low_atom_start();
    len = los_fifo_pop(&msg->fifo, size, (uint8_t*)data);
    if(los_fifo_get_data_length(&msg->fifo)==0)
        msg->sem.lamp_counter = 0;
    losbsp_low_atom_end(low);
    return len;
}
